/**
 * Project3
 *
 * brief description of the program
 *
 * This program is released to public domain according to
 * author's wish, you can view full license at
 * http://tomatoeskit.org/upload/PublicDomainLicense.txt
 *
 * @author Fangning CHENG (Alpha.L Cheng) <chengf@purdue.edu>
 *
 * @lab section number 
 *
 * @date Sep 23, 2013
 *
 */

/**
 * CS 18000 Project 3: Sudoku
 * 
 * <description of your program>
 * 
 * @author <your name>
 * 
 * @lab <section number>
 * 
 * @date <date of completion>
 **/
 
import java.util.Scanner;
import java.io.File;
import java.io.FileNotFoundException;
 
public class Sudoku 
{
    // The dimension of the game grid
    public static final int SIZE = 9;
 
    // The dimension of a subgrid
    public static final int SUBGRID = 3;
 
    // The game grid
    public int[][] game;   
 
    // The original game grid, unmodified by the player
    public int[][] originalGame; 
 
    /**
     * The Constructor.  Reads from file puzzleFile,
     * which contains a whitespace delimited 9x9 grid of 
     * ints whose values are either 1-9 or -1 (to denote
     * an empty block, but store -1).  Store the values in the
     * internal game grids game and originalGame.
     * 
     * Use .nextInt() to read the values in.  This will read the 
     * files in the same order you read text in a book: top to bottom,
     * left to right.
     * 
     * @param puzzleFile: is the name of the file with the puzzle
     **/
    public Sudoku(String puzzleFile)
    {
        // Don't worry about the try {} and catch {} blocks
        // they are necessary for creating the Scanner on a File.
        // Will be covered later.
        try
        {
            // Create a new array of dimensions SIZE x SIZE and assign it to game.
            // Repeat for originalGame.
            game = new int[SIZE][SIZE];
            originalGame = new int[SIZE][SIZE];
            
            //The next two lines are magic
            File file = new File(puzzleFile);
            Scanner in = new Scanner(file);
 
            // Write ONE nested loop (one loop inside another) to traverse both
            // game and originalGame (outer loop: rows, inner loop: columns).
            // Assign the value from the scanner to the correct game[i][j] and 
            // originalGame[i][j]
            
            for (int i = 0; i < SIZE; ++i) {
                for (int j = 0; j < SIZE; ++j) {
                    int element = in.nextInt();
                    originalGame[i][j] = element;
                    game[i][j] = element;
                }
            }
            // See above, you do not need to understand this code yet.  Will
            // be covered later.
            in.close();
        }
        catch (FileNotFoundException e)
        {
            System.out.println("FileNotFound: " + e.getMessage());
        }   
    }
 
    /**
     * Sets each entry in array to 0.  Returns void because arrays are 
     * passed by reference, so the changes are visible to anyone with a copy 
     * of array.
     * 
     * @param array: the array to be modified
     **/
    public void setZero(int[] array)
    {
        // Write a loop to traverse array and assign each entry 0.
        for (int i = 0; i < array.length; ++i)
            array[i] = 0;
    }
 
    private boolean hasNoZero(int[] array)
    {
        for (int i = 1; i < array.length; ++i) {
            if (array[i] == 0)
                return false;
        }
            
        return true;
    }
    
    /**
     * This method determines whether the ints 1-9 are present exactly
     * once in each row.  Sets valSeen[i] = 1 if it sees i.  If at any
     * point valSeen[i] is already 1, the rows are not complete because of 
     * duplicate entries.  
     * 
     * If game[x][y] == -1, there is a blank entry so the row cannot be complete.
     * 
     * @param valSeen: an array of ints that serve as flags to indicate whether
     *                 their entry has been seen before or not.
     * 
     * returns: true if each digit 1-9 is present in the row exactly once, else false
     **/
    public boolean rowsComplete(int[] valSeen)
    {     
        // Write the appropriate nested loops to check the rows.
        // Remember to reset valSeen to 0 after each row.
        setZero(valSeen);
        
        for (int i = 0; i < SIZE; ++i) {
            for (int j = 0; j < SIZE; ++ j) {
                int elementVal = game[i][j];
                if (elementVal == -1)
                    return false;
                if (valSeen[elementVal] == 1)
                    return false;
                else
                    valSeen[elementVal] = 1;

            }
            if (!hasNoZero(valSeen))
                return false;
            setZero(valSeen);
        }
        
        return true; // Change this, placeholder so your code will compile
    }
 
    /**
     * This method determines whether the ints 1-9 are present exactly
     * once in each column.  Sets valSeen[i] = 1 if it sees i.  If at any
     * point valSeen[i] is already 1, the rows are not complete because of 
     * duplicate entries.  
     * 
     * If game[x][y] == -1, there is a blank entry so the row cannot be complete.
     * 
     * @param valSeen: an array of ints that serve as flags to indicate whether
     *                 their entry has been seen before or not.
     * 
     * returns: true if each digit 1-9 is present in the column exactly once, else false
     **/
    public boolean columnsComplete(int[] valSeen)
    {
        // Write the appropriate nested loops to check the columns.
        // Remember to reset valSeen to 0 after each column.
        setZero(valSeen);
        
        for (int i = 0; i < SIZE; ++i) {
            for (int j = 0; j < SIZE; ++ j) {
                int elementVal = game[j][i];
                if (elementVal == -1)
                    return false;
                if (valSeen[elementVal] == 1)
                    return false;
                else
                    valSeen[elementVal] = 1;
            }
            if (!hasNoZero(valSeen))
                return false;
            setZero(valSeen);
        }
        
        return true; // Change this, placeholder so your code will compile
    }
    /**
     * This method determines whether the ints 1-9 are present exactly
     * once in each subgrid.  Sets valSeen[i] = 1 if it sees i.  If at any
     * point valSeen[i] is already 1, the rows are not complete because of 
     * duplicate entries.  
     * 
     * If game[x][y] == -1, there is a blank entry so the row cannot be complete.
     * 
     * @param valSeen: an array of ints that serve as flags to indicate whether
     *                 their entry has been seen before or not.
     * 
     * returns: true if each digit 1-9 is present in each subgrid exactly once, else false
     **/
    public boolean subgridsComplete(int[] valSeen)
    {
        // This will be four nested loops.  The outer two will
        // loop over the rows and columns of subgrids.  The inner
        // two will loop over the rows and columns in each 3x3 subgrid.
        for (int i = 0; i < 3; ++i) {
            for (int j = 0; j < 3; ++j) {
                for (int subI = 0; subI < 3; ++subI) {
                    for (int subJ = 0; subJ < 3; ++subJ) {
                        int elementVal = game[i * 3 + subI][j * 3 + subJ];
                        if (elementVal == -1)
                            return false;
                        if (valSeen[elementVal] == 1)
                            return false;
                        else
                            valSeen[elementVal] = 1;
                        
                    }
                }
                if (!hasNoZero(valSeen))
                    return false;
                setZero(valSeen);
            }
        }       
        return true; // Change this, placeholder so your code will compile
    }
 
    /**
     * Calls rowsComplete(), columnsComplete(), and subgridsComplete()
     * and returns true if they are all true.
     * 
     * returns: true if each row, column, and subgrid is complete, 
     *          else false
     **/
    public boolean isComplete()
    {
        // Create the array valSeen here. I suggest making it = new int[SIZE+1].
        // That way, it will have indexes 0-9, so the ints 1-9 can go into indexes
        // 1-9 instead of mapping them to 0-8 by subtracting 1.
        int[] valSeen = new int[SIZE + 1]; 
        // Call rowsComplete(), columnsComplete(), and subgridsComplete().
        // Be SURE to initialize valSeen to 0 before each method call by using setZero().
        if (!rowsComplete(valSeen))
            return false;
        setZero(valSeen);
        
        if (!columnsComplete(valSeen))
            return false;
        setZero(valSeen);
        
        if (!subgridsComplete(valSeen))
            return false;
        
        return true; // Change this, placeholder so code will compile
    }
 
    /**
     * This is a helper method for print().  It returns a string which contains
     * the first line of the printed grid, "   | a | b | c | d | e | f | g | h | i ".
     * This is useful because the first line is unique in that it only has column 
     * headers, no row headers, so its string is different than the other rows
     * which are created in the print() method.
     * 
     * returns: a String containg the first row of the printed grid:
     *          "   | a | b | c | d | e | f | g | h | i "
     **/
    public String makeHeader()
    {
        // Create the initial string (will not be "", note that the first entry is blank).
        String ret = "   ";
        // Use a loop to add each character in turn to the string, remembering that 'a' + 1 = 'b'.
        for (int i = 0; i < 9; ++i) {
            ret = ret.concat("| " + (char) ('a' + i) + " ");
        }
        return ret; // Change this, placeholder so code will compile
    }
 
    /**
     * Prints out the grid.  Each entry has a space to either side, columns are separated by '|'
     * within the grid / between the header and the grid but not externally.  See the specification 
     * for a detailed example.  -1 is replaced with '_'.
     * 
     * Remember that 'a' + 1 = 'b'
     * 
     * Prints the grid to standard out.
     **/
    public void print()
    {
        // Print the string returned by makeHeader().
        System.out.println(makeHeader());
        // Write a nested loop to print out each entry in the grid
        // in the specified format.  Remember that -1 is printed '_'.
        char lineHeader = 'a';
        for (int i = 0; i < SIZE; ++i) {
            System.out.print(" " + lineHeader + " ");
            ++lineHeader;
            
            for (int j = 0; j < SIZE; ++j) {
                System.out.print("| ");
                int element = game[i][j];
                if (element == -1)
                    System.out.print("_");
                else
                    System.out.print(element);
                System.out.print(" ");
            }
            System.out.println();
        }
    }
 
    /**
     * Checks originalGrid row, col if it is equal to -1, the move is allowed.
     * In which case, it update game row, col to val.  Prints a message:
     * "Fixed location. Cannot change value!" if row, col is a fixed value,
     * ie 1-9 in originalGrid.
     * 
     * You do not have to check for valid input, assume that row and col will
     * be a-i, and val will be 1-9
     * 
     * Hint: the String.charAt(x) method returns the characted at index x in 
     *       the string (strings are 0 indexed).  
     * Hint2: 'b' - 'a' = 1; 'a' - 'a' = 0 etc.
     * 
     * @param row: a string with the row ID in it, ex: "a"
     * @param col: a string with the col ID in it, ex: "b"
     * @param val: the new value of row, col
     * 
     * Does not return, simply updates game[][] if legal.
     **/
    public void move(String row, String col, int val)
    {
        int r = row.charAt(0) - 'a';
        int c = col.charAt(0) - 'a';
        
        if (originalGame[r][c] != -1) {
            System.out.println("Fixed location. Cannot change value!");
            return;
        }
        
        game[r][c] = val;
    }
 
    /**
     * The main method.  Creates an object of class Sudoku, passing args[0] to the constructor.  
     * args[0] is the first commanline argument, in this case the name of a file containing a 
     * 9x9 grid of whitespaced delimited ints of values -1,1-9. 
     * 
     * Prints the existing puzzle, and a puzzle status, either "Puzzle Incomplete!" or
     * "Puzzle Complete" based on whether or the puzzle is complete.
     * 
     * While the puzzle is incomplete, it then prompts the user for input: "Enter new value <row col val> :"
     * and reads in two strings using Scanner.next() and an int using Scanner.nextInt().  
     * These must all be on the same line.  It then makes the specified move and repeats.
     * 
     * @param String[] args: the command line argument, the name of a file containing a puzzle in the 
     *                       specified format.
     * Doesn't return
     **/
    public static void main(String[] args)
    {
        // Create an object of class Sudoku, giving it args[0] as an argument. You will also need a
        // Scanner and some other variables.
 
        Sudoku puzzle = new Sudoku(args[0]);  // replace null with an instantiation of Sudoku (e.g. new Sudoku(...))
        Scanner scanner = new Scanner(System.in);
        
        while (!puzzle.isComplete()) {
            puzzle.print();
            System.out.println("Puzzle Incomplete!");
            System.out.println("Enter new value <row col val> :");
            String row = scanner.next();
            String col = scanner.next();
            int val = scanner.nextInt();
            puzzle.move(row, col, val);
        }
        scanner.close();
        puzzle.print();
        System.out.println("Puzzle Complete!");
 
        // Print the puzzle, then create a loop prompting for moves while the puzzle is incomplete.
    }
    
}